﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;
using DotNet.Utilities.WpfHelpers;
using Newtonsoft.Json;
using PropertyChanged;
using XmppClient_agsXmpp.Properties;
using XmppClient_agsXmpp.UserControls;

namespace XmppClient_agsXmpp.ViewModels
{
    [AddINotifyPropertyChangedInterface]
    public class MainWindowViewModel : BindableBase
    {
        #region 成员

        private XmppClientConnection xmpp = new XmppClientConnection();

        private string _JidStr => $"{UserId}@{Url}";

        #endregion

        #region Bindable

        /// <summary>
        /// XMPP服务器（如Openfire）地址
        /// </summary>
        public string Url { get; set; } = "192.168.16.120";

        public string UserName { get; set; }

        public string Password { get; set; }

        /// <summary>
        /// 用户ID（@前的部分）
        /// </summary>
        public string UserId { get; set; }

        /// <summary>
        /// 发送的内容
        /// </summary>
        public string ChatContent { get; set; }

        /// <summary>
        /// 信息窗内容;
        /// </summary>
        public string Info { get; set; }

        public ObservableCollection<string> Friends { get; set; } = new ObservableCollection<string>();

        public XmppConnectionState State { get; set; } = XmppConnectionState.Disconnected;

        public ConfirmBoxViewModel DialogVm { get; set; } = new ConfirmBoxViewModel();

        #endregion

        #region Command

        public ICommand LoginCommand { get; set; }

        public ICommand AddFriendCommand { get; set; }

        public ICommand SendMessageCommand { get; set; }

        #endregion

        public MainWindowViewModel()
        {
            SetCommandMethod();

            InitValue();
            InitXmpp(xmpp);
        }

        private void InitValue()
        {
            if (!string.IsNullOrWhiteSpace(Settings.Default.Url))
            {
                Url = Settings.Default.Url;
            }

            if (!string.IsNullOrWhiteSpace(Settings.Default.UserName))
            {
                UserName = Settings.Default.UserName;
            }

            if (!string.IsNullOrWhiteSpace(Settings.Default.Password))
            {
                Password = Settings.Default.Password;
            }
        }

        /// <summary>
        /// 命令方法赋值(在构造函数中调用)
        /// </summary>
        private void SetCommandMethod()
        {
            LoginCommand ??= new RelayCommand(o => !(string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password)), async o=>
            {
                await ConfirmBoxHelper.ShowMessage(DialogVm, "操作前通知", 6);

                await ConfirmBoxHelper.ShowConfirm(DialogVm, "您确定要进行此操作吗？", () =>
                {
                    #region 业务方法

                    xmpp.Close();

                    xmpp.Username = UserName;
                    xmpp.Password = Password;
                    xmpp.Server = Url;

                    Settings.Default.Url = Url;
                    Settings.Default.UserName = UserName;
                    Settings.Default.Password = Password;
                    Settings.Default.Save();

                    xmpp.Open();

                    #endregion

                }, ShowInfo);

                await ConfirmBoxHelper.ShowMessage(DialogVm, "操作后通知");
            });

            AddFriendCommand ??= new RelayCommand(o => true, o =>
            {
                PresenceManager pm = new PresenceManager(xmpp);
                pm.Subscribe(new Jid(_JidStr));
                ShowInfo($"已向[{_JidStr}]发送好友申请！");
            });

            SendMessageCommand ??= new RelayCommand(o => true, o =>
            {
                xmpp.Send(new agsXMPP.protocol.component.Message(new Jid($"{_JidStr}"), MessageType.chat, ChatContent));
                ShowInfo($"已向[{_JidStr}]发送消息！");
            });
        }

        #region XMPP事件
        /***
         * （参考1：https://www.cnblogs.com/wj-love/archive/2012/06/25/2561561.html）
         * （参考2：YB）
         */

        /// <summary>
        /// Xmpp初始化设置;
        /// </summary>
        /// <param name="xmpp"></param>
        private void InitXmpp(XmppClientConnection xmpp)
        {
            xmpp.KeepAliveInterval = 60;

            //登陆成功事件（登陆 Openfire 3.9.3 能触发该事件，登陆 Openfire 4.5.2 则不能（后来又可以了））
            xmpp.OnLogin += new ObjectHandler(sender =>
            {
                ShowInfo($"触发 OnLogin 事件");
                OnLoginSuccess(xmpp);
            });

            xmpp.OnAuthError += (sender, element) =>
            {
                ShowInfo($"验证失败!{JsonConvert.SerializeObject(element)}");
            };

            xmpp.OnClose += sender =>
            {
                ShowInfo($"退出登陆!");

                // 通知好友，我下线啦 
                Presence p = new Presence(ShowType.chat, "Offline") { Type = PresenceType.unavailable };
                xmpp.Send(p);
            };

            xmpp.OnError += (sender, exception) =>
            {
                ShowInfo($"发生错误!{exception.Message}");
            };

            xmpp.OnXmppConnectionStateChanged += Xmpp_OnXmppConnectionStateChanged;
            xmpp.OnPresence += Xmpp_OnPresence; //出席事件（如联系人上线等）
            xmpp.OnRosterItem += Xmpp_OnRosterItem;
            xmpp.OnMessage += Xmpp_OnMessage;
        }

        /// <summary>
        /// 登陆成功处理逻辑;
        /// </summary>
        /// <param name="xmpp"></param>
        private void OnLoginSuccess(XmppClientConnection xmpp)
        {
            ShowInfo($"登陆成功!");

            // 通知好友，我已经上线了 
            Presence p = new Presence(ShowType.chat, "Online") { Type = PresenceType.available };
            xmpp.Send(p);
        }

        /// <summary>
        /// 连接状态改变事件;
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="state"></param>
        private void Xmpp_OnXmppConnectionStateChanged(object sender, XmppConnectionState state)
        {
            State = state;
            ShowInfo($"连接状态改变为：{State.ToString()}");

            //if (state == XmppConnectionState.StartSession)
            //{
            //    OnLoginSuccess(xmpp);
            //}

            if (state == XmppConnectionState.SessionStarted)
            {
                ShowInfo($"会话启动成功!");
            }
        }

        /// <summary>
        /// 收到 Presence 节时引发的事件，判断用户上线 / 离线触发，以及用户处理好友请求 (拒绝 / 接受)、被 T 处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="pres"></param>
        private void Xmpp_OnPresence(object sender, Presence pres)
        {
            ShowInfo($"出现!消息类型：{pres.Type.ToString()}");

            //**************** 处理好友请求 ************************// 
            if (pres.Type == PresenceType.subscribe)// 收到好友请求 
            {
                ShowInfo($"收到[{pres.From.Bare}]的好友申请！将自动同意。");

                //同意好友申请;
                PresenceManager pm = new PresenceManager(xmpp);
                pm.ApproveSubscriptionRequest(pres.From);
            }
            else if (pres.Type == PresenceType.subscribed) //好友申请已通过;
            {
                ShowInfo($"[{pres.From.Bare}]通过了您的好友申请！");
            }
            else if (pres.Type == PresenceType.unsubscribe)
            {
                ShowInfo($"对[{pres.From.Bare}]的好友申请已被拒绝！");
            }
            else if (pres.Type == PresenceType.unsubscribed)// 收到 “拒绝请求” 或者 “被 T” 的时候 
            {
                ShowInfo($"已被[{pres.From.Bare}]解除好友！");
            }
            else// 处理好友上线 / 离线的事件处理 
            {
                if (pres.From.Bare.Equals(xmpp.MyJID.Bare) == false)// 如果不是自己的信息，即好友信息 
                {
                    // 检测该好友是否不存在于好友列表 (OnPresence 比 OnRosterItem 优先处理添加好友)，则自动添加 ListItem 
                    if (Friends.IndexOf(pres.From.Bare) == -1)
                    {
                        // 添加到好友列表 
                        Friends.Add(pres.From.Bare);
                    }

                    // 判断该好友目前的状态，上线还是离线 
                    if (pres.Type == PresenceType.available) // 如果好友上线 
                    {
                        ShowInfo($"好友[{pres.From.Bare}]上线！");
                    }
                    else if (pres.Type == PresenceType.unavailable) // 如果好友离线 
                    {
                        ShowInfo($"好友[{pres.From.Bare}]下线！");
                    }
                }
            }
        }

        /// <summary> 
        /// 更新好友列表的事件 (有多少好友就会触发多少次)
        /// </summary> 
        private void Xmpp_OnRosterItem(object sender, agsXMPP.protocol.iq.roster.RosterItem item)
        {
            /*
            1、 None——Jabber 用户和联系人互相都不订阅对方的 presence
            2、 To——Jabber 用户被联系人的 presence 订阅，但联系人并不被 Jabber 用户的 presence 订阅。
            3、 From—— 联系人接受来自 Jabber 用户的 subscription，但联系人不被 Jabber 用户的 presence 所订阅（这样的结果就是 Jabber 用户在联系人的花名册中 “不可见”）。
            4、 Both——Jabber 用户和联系人被彼此的 presence 订阅。
            */
            if (item.Subscription == SubscriptionType.both)// 当 item 所表示的好友都彼此加为好友 
            {
                // 添加到好友列表 
                Friends.Add(item.Jid.Bare);
            }
        }

        /// <summary>
        /// 收到消息触发事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="msg"></param>
        private void Xmpp_OnMessage(object sender, Message msg)
        {
            ShowInfo($"收到来自[{msg.From.Bare}]的消息，类型为[{msg.Type}]，内容为：\r\n{msg.Body}");

            if (msg.Type == MessageType.chat)// 判断聊天类型 
            {
                //如果还没与该好友对话，则新建聊天窗口；已经存在聊天窗口，则把内容传到聊天窗口显示 
            }
            else if (msg.Type == MessageType.normal)
            {

            }
            else if (msg.Type == MessageType.error)
            {

            }
        }

        #endregion

        #region XMPP方法

        /// <summary>
        /// XMPP 发送方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        private bool XmppSend<T>(T data, string userId)
        {
            try
            {
                if (data == null)
                {
                    return false;
                }

                string outMessage = JsonConvert.SerializeObject(data);
                xmpp.Send(new agsXMPP.protocol.client.Message(new Jid($"{userId}@{Url}"), MessageType.chat, outMessage));

                return true;
            }
            catch (Exception ex)
            {
                ShowInfo($"发生数据出错：{ex.Message}");
                return false;
            }
        }

        #endregion

        #region 辅助方法

        private void ShowInfo(string info)
        {
            Info += $"{info}\r\n\r\n";
        }

        #endregion
    }
}
